package org.balthie.cal.core;

import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

import org.balthie.cal.common.ex.InputException;
import org.balthie.cal.core.IMathOperator.MathOperatorType;
import org.balthie.cal.impl.io.ConsoleOutputer;
import org.balthie.cal.impl.io.GenericLiterallyButton;

public abstract class AbsCalculator implements ICalculator {

	/**
	 * 指令映射表 key: 输入设备（计算器按键） value: 计算器内部指令
	 */
	protected Map<IInputer, ICommand> commandDict = new HashMap<IInputer, ICommand>(64);

	/**
	 * 计算器的键盘
	 */
	protected Map<String, IInputer> keyboard = new HashMap<String, IInputer>(256);

	/**
	 * 计算器的显示栏：默认使用Console
	 */
	protected IOutputer defaultOutputer = new ConsoleOutputer();

	/**
	 * 指令集暂存区：用户已输入的指令，按时间顺序排列
	 */
	private static ThreadLocal<Deque<ICommand>> cmds = new ThreadLocal<Deque<ICommand>>() {
		@Override
		protected Deque<ICommand> initialValue() {
			return new LinkedList<ICommand>();
		}
	};

	/**
	 * 回退指令集暂存区：执行undo后撤销的指令，按时间顺序排列
	 */
	private static ThreadLocal<Deque<ICommand>> undoCmds = new ThreadLocal<Deque<ICommand>>() {
		@Override
		protected Deque<ICommand> initialValue() {
			return new LinkedList<ICommand>();
		}
	};

	@Override
	public Map<String, IInputer> getKeyboard() {
		return keyboard;
	}

	@Override
	public IOutputer getDisplayBar() {
		return defaultOutputer;
	}

	protected void registGenericInputer(String displayName, ICommand bindCommand) {
		IInputer input = new GenericLiterallyButton(displayName, this);
		bindCmd(input, bindCommand);
	}

	protected void bindCmd(IInputer inputer, ICommand cmd) {
		try {
			this.keyboard.put(inputer.getDisplayName(), inputer);
			registCommandDict(inputer, cmd);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	protected void registCommandDict(IInputer inputer, ICommand cmd) {
		this.commandDict.put(inputer, cmd);
		AbsCommand absCmd = (AbsCommand) cmd;
		absCmd.cal = this;
	}

	void onInput(IInputer input) {
		// 根据输入，获取指令
		ICommand cmd = this.parseCommand(input);
		executeCmds(cmd);
	}

	protected void executeCmds(ICommand cmd) {
		try {
			// 检查新指令合法性
			cmd.isLegal(cmds.get(), undoCmds.get());
			// 工作指令集size校验
			cmdsLengthCheck(cmd);

			// 检查算式完整性
			if (cmd.isCompleted(cmds.get())) {
				// 完整算式，执行指令集
				cmd.execute(cmds.get(), undoCmds.get());
			} else {
				if (ICalKind.CalKind.Controllor == cmd.getCalKind()) {
					// 控制指令，单独运行
					cmd.execute(cmds.get(), undoCmds.get());
				} else {
					if (!MathOperatorType.Equals.literally.equals(cmd.getLiterally())) {
						// 非控制指令，指令入列
						// =号同时有控制指令作用，也不入列
						cmds.get().add(cmd);

						// 清空回退指令暂存区： 防止 undo，redo 中间有新的输入
						if (!undoCmds.get().isEmpty())
							undoCmds.get().clear();
					}
				}
			}
		} catch (ArithmeticException e) {
			//数学运算异常，清空指令区
			cmds.get().clear();
			undoCmds.get().clear();
		} catch (Exception e) {
			// 非法指令抛出异常
			this.getDisplayBar().printlnErr(e);
			throw e;
		}
	}

	protected ICommand parseCommand(IInputer input) {
		ICommand cmd = commandDict.get(input);
		if (cmd == null) {
			throw new InputException(String.format("非法输入[%s]：未配置指令", input.getDisplayName()));
		}
		return cmd;
	}

	protected void println(Object... args) {
		getDisplayBar().println(args);
	}

	protected void print(Object... args) {
		getDisplayBar().print(args);
	}
	
	// 清理线程 ThreadLocal	
	protected void clear()
	{
		cmds.get().clear();
		undoCmds.get().clear();
	}
	
	private void cmdsLengthCheck(ICommand cmd) {
		if (cmds.get().size() > this.getMaxCommandLenght()-1 && ICalKind.CalKind.Controllor != cmd.getCalKind() && !MathOperatorType.Equals.literally.equals(cmd.getLiterally())) {
			throw new InputException(String.format("算式过长: 最多支持输入[%s]个字符", this.getMaxCommandLenght()));
		}
	}

	public static enum StandardInputerKey {
		Unknown("unknown"),
		// 数字
		Numb_0("0"), Numb_1("1"), Numb_2("2"), Numb_3("3"), Numb_4("4"), Numb_5("5"), Numb_6("6"), Numb_7("7"),
		Numb_8("8"), Numb_9("9"),
		// 基础算术符
		MathOpt_Add("+"), MathOpt_Sub("-"), MathOpt_Mul("*"), MathOpt_Div("/"), MathOpt_EQ("="),
		// 功能符
		Ctrl_Echo("echo"), Ctrl_Redo("redo"), Ctrl_Undo("undo"), Ctrl_Help("help"),
		//
		;

		String key;

		private StandardInputerKey(String key) {
			this.key = key;
		}

		public String getKey() {
			return key;
		}
	}
}
